using System;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;
using WeifenLuo.WinFormsUI;
using PluginCore;
using PluginCore.Controls;

namespace haxeProject {

	public class PluginMain : IPlugin {

		private string pluginName = "haXe Project";
		private string pluginGuid = "8D0841F4-0D9A-4FA5-BC38-40554E4497CA";
		private string pluginGuid2 = "46F61047-5FC7-4FB1-A53D-14B9B9D3F005";
		private string pluginAuth = "Nicolas Cannasse";
		private string pluginHelp = "http://haxe.org/flashdevelop";
		private string pluginDesc = "haXe Project Manager for FlashDevelop";
		private EventType eventMask = 0
			| EventType.FileOpening
			| EventType.ProcessEnd
		;
		private IPluginHost pluginHost;
		private IMainForm form;
		private DockContent panel;
		private DockContent fpanel;
		private PluginUI ui;
		private ProjectFiles files;
		private ProjectMenu pmenu;
		public static ProjectData project;
		public bool building;
		public bool testing;
		
		#region RequiredPluginVariables

		public string Name
		{
			get { return this.pluginName; }
		}

		public string Guid
		{
			get { return this.pluginGuid; }
		}

		public string Author
		{
			get { return this.pluginAuth; }
		}

		public string Description
		{
			get { return this.pluginDesc; }
		}

		public string Help
		{
			get { return this.pluginHelp; }
		}

		public EventType EventMask
		{
			get { return this.eventMask; }
		}

		[Browsable(false)]
		public IPluginHost Host
		{
			get { return this.pluginHost; }
			set	{ this.pluginHost = value; }
		}

		[Browsable(false)]
		public DockContent Panel
		{
			get { return this.panel; }
		}

		#endregion

		#region RequiredPluginMethods

		public void Initialize() {

			this.form = this.pluginHost.MainForm;

			Icons.Init(form);
			this.ui = new PluginUI(this);
			this.files = new ProjectFiles(this);

			// create panel
			this.ui.Tag = "haXe Project";
			this.ui.Text = "haXe Project";
			this.panel = form.CreateDockingPanel(this.ui,this.pluginGuid,Icons.PANEL.image,DockState.DockLeftAutoHide);

			// files panel
			this.files.Tag = "haXe Sources";
			this.files.Text = "haXe Sources";
			this.fpanel = form.CreateDockingPanel(this.files,this.pluginGuid2,Icons.FPANEL.image,DockState.DockLeftAutoHide);

			// add haxe menus
			pmenu = new ProjectMenu(form.GetCBMainMenu());
			pmenu.mnew.Click += new EventHandler(onNewProject);
			pmenu.view.Click += new EventHandler(OpenPanel);
			pmenu.fview.Click += new EventHandler(OpenPanel);
			pmenu.open.Click += new EventHandler(onProjectOpen);
			pmenu.close.Click += new EventHandler(onProjectClose);
			pmenu.build.Click += new EventHandler(onProjectBuild);
			pmenu.test.Click += new EventHandler(onProjectTest);
			pmenu.buildTest.Click += new EventHandler(onProjectBuild);
			pmenu.enabled = true;
			form.IgnoredKeys.Add(pmenu.buildTest.Shortcut);
			pmenu.enabled = false;

			// completion bindings
			UITools.OnCharAdded += new UITools.CharAddedHandler( OnChar );
			tip_timer = new Timer();
			tip_timer.Interval = 100;
			tip_timer.Tick += new EventHandler(onTipTimer);
			tip_timer.Start();
			
			closeProject();
		}

		public void Dispose() {
			if( project != null ) {
				project.save();
				project = null;
			}
		}

		public void HandleEvent(object sender, NotifyEvent e) {
			TextEvent te = e as TextEvent;
			try {
			switch( te.Type ) {
			case EventType.FileOpening:
				if( Path.GetExtension(te.Text).ToLower() != ".hxp" )
					return;
				te.Handled = true;
				openProject(te.Text,false);
				break;
			case EventType.ProcessEnd:
				if( !building )
					return;
				building = false;
				bool success = te.Text.EndsWith("(0)");
				status(success?"Done":"Compilation Failed");
				if( success && testing )
					onProjectTest(null,null);
				break;
			}
			} catch( Exception exc ) {
				Dialog.Error(exc.ToString());
			}
		}

		public void OpenPanel(object sender, System.EventArgs e) {
			if( sender == pmenu.view )
				this.panel.Show();
			else
				this.fpanel.Show();
		}

		#endregion

		#region project API

		public void addOutput() {
			project.outputs.Add(new ProjectOutput("New target"));
			refreshOutputs();
			TreeNode n = ui.Outputs.Nodes[0].Nodes[project.outputs.Count-1];
			n.EnsureVisible();
			n.BeginEdit();
		}
		
		public void toggleOutput( ProjectOutput o ) {
			o.active = !o.active;
			refreshOutputs();
		}

		public void selectOutput( ProjectOutput o ) {
			project.selectedOutput = project.outputs.IndexOf(o);
			refreshOutputs();
		}

		public void deleteOutput( ProjectOutput o ) {
			project.outputs.Remove(o);
			refreshOutputs();
			if( ui.Props.Tag == o )
				ui.Props.Visible = false;
		}

		void onNewProject( object sender, EventArgs e ) {
			String file = Dialog.Create(Dialog.PROJECT,null);
			if( file == null )
				return;
			openProject(file,true);
		}

		void onProjectOpen(object sender, EventArgs e) {
			String file = Dialog.Open(Dialog.PROJECT);
			if( file == null )
				return;
			openProject(file,false);
		}

		void onProjectClose(object sender, EventArgs e) {
			closeProject();
		}

		void onProjectBuild(object sender, EventArgs e) {
			form.CallCommand("SaveAllModified",null);
			status("Compiling...");
			string process = Environment.GetEnvironmentVariable("HAXEPATH")+"haxe.exe";
			string hxml;
			try {
				hxml = project.buildHXML();
			} catch( Exception error ) {
				Dialog.Error(error.Message);
				return;
			}
			Environment.CurrentDirectory = Path.GetDirectoryName(hxml);
			building = true;
			testing = (sender == pmenu.buildTest);			
			form.CallCommand("RunProcessCaptured", process + ";" + Path.GetFileName(hxml));
		}

		void onProjectTest( object sender, EventArgs e ) {
			ProjectOutput o = project.outputs[project.selectedOutput] as ProjectOutput;
			if( o == null )
				return;
			status("Testing "+o.name);
			Environment.CurrentDirectory = Path.GetDirectoryName(project.file);
			if( o.mode == ProjectOutput.NEKO && o.command == "" ) {
				string neko = null;
				foreach( String dir in Environment.GetEnvironmentVariable("PATH").Split(';') ) {
					neko = dir+"/neko.exe";
					if( File.Exists(neko) )
						break;
					neko = null;
				}
				if( neko == null ) {
					Dialog.Error("Neko.exe not found in PATH");
					return;
				}
				form.CallCommand("RunProcessCaptured", neko + ";" + o.output);
			} else {
				String cmd = "", args = "";
				try {
					Process p;
					if( o.command == "" )
						cmd = o.output;
					else {
						string [] cmdline = o.command.Split(' ');
						for( int i = 1; i < cmdline.Length; i++) {
							if( i > 1 )
								args += " ";
							args += cmdline[i];
						}
						cmd = cmdline[0];
					}	
					p = Process.Start(cmd.Replace('/','\\'),args);
				} catch( Exception error ) {
					Dialog.Error("Error While Running \""+cmd+"\" "+args+" : "+error.Message);
				}
			}
		}

		void status( String text ) {
			form.StatusBar.Panels[0].Text = "  " + text;
		}

		void openProject( String file, bool create ) {
			closeProject();			
			project = new ProjectData(file,create);
			pmenu.enabled = true;
			files.Visible = true;
			ui.Visible = true;
			ui.PName.Text = Path.GetFileName(file);
			refreshOutputs();
			refreshFiles();
		}

		void closeProject() {
			if( project != null ) {
				project.save();
				project = null;
			}
			pmenu.enabled = false;
			ui.Visible = false;
			files.Visible = false;
		}

		void refreshOutputs() {
			Node root = new Node("Project '"+Path.GetFileName(project.file)+"' Targets");
			int i = 0;
			ui.Outputs.Nodes.Clear();
			ui.Outputs.Nodes.Add(root);
			foreach( ProjectOutput p in project.outputs ) {
				Node n = new Node(p.name);
				root.Nodes.Add(n);
				n.Tag = p;
				n.bold = false;
				if( !p.active )
					n.icon = Icons.REMOVE;
				else if( i++ == project.selectedOutput )
					n.icon = Icons.CUR_OUTPUT;
				else
					n.icon = Icons.OUTPUT;				
			}
			root.icon = Icons.PANEL;
			root.bold = false;
			root.EnsureVisible();
			root.Expand();
		}

		public void refreshProperties( ProjectOutput p ) {
			ui.Props.Tag = p;
			if( p == null ) {
				ui.Props.Visible = false;
				return;
			}
			ui.Props.Visible = true;
			ui.Props.Text = "Target "+p.name;
			ui.pOutput.Text = p.output;
			if( p.mode == ProjectOutput.FLASH )
				ui.pFlash.Checked = true;
			else if( p.mode == ProjectOutput.FLASH9 )
				ui.pFlash9.Checked = true;
			else if( p.mode == ProjectOutput.JS )
				ui.pJS.Checked = true;
			else if( p.mode == ProjectOutput.PHP )
				ui.pPHP.Checked = true;
			else
				ui.pNeko.Checked = true;
			ui.pClass.Text = p.mainClass;
			ui.pLib.Text = p.library;
			ui.pMain.Checked = p.mainCall;
			ui.pDebug.Checked = p.debug;
			ui.pExtra.Text = p.extra;
			ui.pCommand.Text = p.command;
		}
		
		#endregion
		
		#region project Files API

		String RelativePath( String file, String path ) {			
			if( file.StartsWith(path) ) {
				int l = path.Length + 1;
				return file.Substring(l,file.Length-l);
			}
			int p = path.LastIndexOf('\\');
			int p2 = path.LastIndexOf('/');
			if( p2 > p )
				p = p2;
			if( p == -1 )
				return file; // other drive ?
			String rel = RelativePath(file,path.Substring(0,p-1));
			if( rel != file ) {
				if( rel.StartsWith("\\") || rel.StartsWith("/") )
					return ".."+rel;
				return "..\\"+rel;
			}
			return file;
		}
		
		public bool addFile( String path, ProjectFile parent, bool create ) {
			String baseDir = Path.GetDirectoryName(project.file);
			if( baseDir.EndsWith("\\") )
				baseDir = baseDir.Substring(0,baseDir.Length - 1);			
			path = RelativePath(path,baseDir);
			if( project.GetSubFile(path,project.root) != null )
				return false;
			if( create )
				File.Create(baseDir+"/"+path).Close();
			ProjectFile f = new ProjectFile(path,false);
			parent.Add(f);
			refreshFiles();
			files.Get(f).EnsureVisible();
			// open file for editing
			openFile(f);
			return true;
		}
		
		public void addDir( ProjectFile parent ) {
			ProjectFile f = new ProjectFile("New Directory",true);
			parent.Add(f);
			refreshFiles();
			Node n = files.Get(f);
			n.EnsureVisible();
			n.BeginEdit();
		}
		
		public void openFile( ProjectFile f ) {
			if( f == null || f.isFolder )
				return;
			String path;
			if( f.path.IndexOf(':') != -1 )
				path = f.path;
			else
				path = Path.GetDirectoryName(project.file)+"\\"+f.path;
			form.OpenSelectedFile(Path.GetFullPath(path));
		}

		public void removeFile( ProjectFile f ) {
			f.parent.Remove(f);
			refreshFiles();
		}
		
		public void moveFile( ProjectFile f, ProjectFile target ) {
			ProjectFile tmp = target;
			while( tmp != null ) {
				if( tmp == f )
					return;
				tmp = tmp.parent;
			}  
			f.parent.Remove(f);
			target.Add(f);
			refreshFiles();
			files.Get(f).EnsureVisible();			
		}
		
		void refreshFiles() {
			files.Files.Nodes.Clear();
			files.Files.Nodes.Add(refreshFile(project.root));
		}

		Node refreshFile( ProjectFile f ) {
			Node n = new Node(f.ToString());
			n.Tag = f;
			n.icon = f.isFolder?Icons.FILE_FOLDER:Icons.FILE_SOURCE;
			if( !f.isFolder )
				return n;
			if( f == project.root ) {
				n.Text = "Project '"+Path.GetFileName(project.file)+"'";
				n.icon = Icons.FPANEL;
			}
			f.Sort();
			foreach( ProjectFile c in f.childs )
				n.Nodes.Add(refreshFile(c));
			if( f.expanded )
				n.Expand();
			return n;
		}
		public String generateHtml( ProjectOutput o ) {
			String ppath = Path.GetDirectoryName(project.file);
			String opath = Path.GetDirectoryName(o.output);
			Environment.CurrentDirectory = ppath;
			if( opath != "" )
				Environment.CurrentDirectory = opath;
			String file = Path.GetFileNameWithoutExtension(o.output)+".html";
			if( File.Exists(file) && !Dialog.Confirm("Overwrite existing html file ?") )
				return null;			
			StreamWriter s = File.CreateText(file);
			s.WriteLine("<html>");
			s.WriteLine("<body>");
			if( o.mode == ProjectOutput.JS ) {				
				s.WriteLine("<div id=\"haxe:trace\"></div>");
				s.WriteLine("<script type=\"text/javascript\" src=\""+Path.GetFileName(o.output)+"\"></script>");
			} else {
				String swf = Path.GetFileName(o.output);
				String id = "haxePath";
				String color = "FFFFFF";
				int width = 400;
				int height = 300;
				
				s.WriteLine("<object");
				s.WriteLine("  classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\"");
				s.WriteLine("  codebase=\"http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0\"");
				s.WriteLine("  width=\""+width+"\"");
				s.WriteLine("  height=\""+height+"\"");
				s.WriteLine("  id=\""+id+"\"");
				s.WriteLine(">");
				s.WriteLine("    <param name=\"movie\" value=\""+swf+"\">");
				s.WriteLine("    <param name=\"bgcolor\" value=\""+color+"\">");
				s.WriteLine("    <param name=\"scale\" value=\"noscale\">");
				s.WriteLine("    <param name=\"salign\" value=\"lt\">");
				s.WriteLine("    <param name=\"menu\" value=\"true\">");
				s.WriteLine("    <param name=\"quality\" value=\"high\">");
				s.WriteLine("    <param name=\"allowScriptAccess\" value=\"always\">");
				s.WriteLine("    <embed");
				s.WriteLine("      src=\""+swf+"\"");
				s.WriteLine("      width=\""+width+"\"");
				s.WriteLine("      height=\""+height+"\"");
				s.WriteLine("      bgcolor=\""+color+"\"");
				s.WriteLine("      name=\""+id+"\"");
				s.WriteLine("      scale=\"noscale\"");
				s.WriteLine("      salign=\"lt\"");
				s.WriteLine("      menu=\"true\"");
				s.WriteLine("      quality=\"high\"");
				s.WriteLine("      swLiveConnect=\"true\"");
				s.WriteLine("      allowScriptAccess=\"always\"");
				s.WriteLine("      type=\"application/x-shockwave-flash\"");
				s.WriteLine("      pluginspage=\"http://www.macromedia.com/go/getflashplayer\"");
				s.WriteLine("</object>");
			}
			s.WriteLine("</body>");
			s.WriteLine("</html>");
			s.Close();
			return RelativePath(Environment.CurrentDirectory+"/"+file,ppath);
		}

		#endregion
		
		#region Completion

		TipThread tip_thread;
		Timer tip_timer;
		
		void onProjectTip( ScintillaNet.ScintillaControl sci ) {
			form.CallCommand("SaveAllModified",null);
			if( tip_thread != null )
				return;
			string process = Environment.GetEnvironmentVariable("HAXEPATH")+"haxe.exe";
			string hxml;
			try {
				hxml = project.buildHXML();
			} catch( Exception error ) {
				Dialog.Error(error.Message);
				return;
			}
			Environment.CurrentDirectory = Path.GetDirectoryName(hxml);
			int pos = sci.CurrentPos;
			if( sci.Encoding == System.Text.Encoding.UTF8 )
				pos += 3; // BOM
			string file = sci.FileName;
			tip_thread = new TipThread(sci,process,"--display \""+file+"@"+pos+"\" "+Path.GetFileName(hxml));
			tip_thread.start();
		}
		
		void onTipTimer( Object sender, EventArgs args ) {
			if( tip_thread == null || tip_thread.data == null )
				return;
			if( tip_thread.sci.CurrentPos == tip_thread.pos ) {
				if( tip_thread.tips != null )
					CompletionList.Show(tip_thread.tips,true);
				else if( tip_thread.type != null )
					InfoTip.CallTipShow(tip_thread.sci,tip_thread.pos,tip_thread.type);
				else if( tip_thread.data != "" )
					InfoTip.CallTipShow(tip_thread.sci,tip_thread.pos,tip_thread.data);
			}
			tip_thread = null;
		}


		private void OnChar(ScintillaNet.ScintillaControl sci, int c ) {
			if( project == null )
				return;
			if( c == '.' || c == '(' )
				onProjectTip(sci);
			else if( c == ';' || c == '\n' || c == ')' ) {
				tip_thread = null;			
				InfoTip.Hide();
			} else
				tip_thread = null;
		}

		#endregion

		
	}

}
